﻿using System;

public sealed class PAPU
{
    internal int accCount;
    internal sbyte b1;
    internal sbyte b2;
    internal sbyte b3;
    internal sbyte b4;
    internal int bufferIndex;
    internal int bufferSize = 0x800;
    internal short channelEnableValue;
    internal int countSequence;
    internal Memory cpuMem;
    internal int dacRange;
    internal int dcValue;
    internal int derivedFrameCounter;
    internal ChannelDM dmc;
    internal int[] dmcFreqLookup;
    internal int extraCycles;
    internal bool frameClockNow;
    internal bool frameIrqActive;
    internal int frameIrqCounter;
    internal int frameIrqCounterMax;
    internal bool frameIrqEnabled;
    internal int frameTime;
    internal int initCounter;
    internal bool initingHardware;
    internal int[] ismpbuffer;
    public bool isRunning;
    internal int[] LengthLookup;
    internal MyMediaStreamSource line;
    internal int masterFrameCounter;
    internal int masterVolume;
    internal int maxCycles;
    internal NES nes;
    internal ChannelNoise noise;
    internal int[] noiseWaveLengthLookup;
    internal int[] panning;
    internal int prevSampleL;
    internal int prevSampleR;
    internal bool recordOutput;
    internal byte[] sampleBuffer;
    internal int sampleCount;
    internal int sampleRate = 0xac44;
    internal int sampleTimer;
    internal int sampleTimerMax;
    internal int sampleValueL;
    internal int sampleValueR;
    internal int smpAccumL;
    internal int smpAccumR;
    internal int smpDiffL;
    internal int smpDiffR;
    internal int smpDmc;
    internal int smpNoise;
    internal int smpSquare1;
    internal int smpSquare2;
    internal int smpTriangle;
    internal int sq_index;
    internal int[] square_table;
    internal ChannelSquare square1;
    internal ChannelSquare square2;
    internal bool startedPlaying;
    internal bool stereo = true;
    internal int stereoPosLDMC;
    internal int stereoPosLNoise;
    internal int stereoPosLSquare1;
    internal int stereoPosLSquare2;
    internal int stereoPosLTriangle;
    internal int stereoPosRDMC;
    internal int stereoPosRNoise;
    internal int stereoPosRSquare1;
    internal int stereoPosRSquare2;
    internal int stereoPosRTriangle;
    internal int tnd_index;
    internal int[] tnd_table;
    internal ChannelTriangle triangle;
    internal int triValue;
    public bool userEnableDmc = true;
    private bool userEnableNoise = true;
    private bool userEnableSquare1 = true;
    private bool userEnableSquare2 = true;
    private bool userEnableTriangle = true;

    public PAPU(NES nes)
    {
        this.nes = nes;
        this.cpuMem = nes.CpuMemory;
        this.setSampleRate(this.sampleRate, false);
        this.sampleBuffer = new byte[this.bufferSize * (this.stereo ? 4 : 2)];
        this.ismpbuffer = new int[this.bufferSize * (this.stereo ? 2 : 1)];
        this.bufferIndex = 0;
        this.frameIrqEnabled = false;
        this.initCounter = 0x800;
        this.square1 = new ChannelSquare(this, true);
        this.square2 = new ChannelSquare(this, false);
        this.triangle = new ChannelTriangle(this);
        this.noise = new ChannelNoise(this);
        this.dmc = new ChannelDM(this);
        this.masterVolume = 0x100;
        this.panning = new int[] { 80, 170, 100, 150, 0x80 };
        this.Panning = this.panning;
        this.initLengthLookup();
        this.initDmcFrequencyLookup();
        this.initNoiseWaveLengthLookup();
        this.initDACtables();
        this.frameIrqEnabled = false;
        this.frameIrqCounterMax = 4;
    }

    private void accSample(int cycles)
    {
        if (this.triangle.sampleCondition)
        {
            this.triValue = (this.triangle.progTimerCount << 4) / (this.triangle.progTimerMax + 1);
            if (this.triValue > 0x10)
            {
                this.triValue = 0x10;
            }
            if (this.triangle.triangleCounter >= 0x10)
            {
                this.triValue = 0x10 - this.triValue;
            }
            this.triValue += this.triangle.sampleValue;
        }
        if (cycles == 2)
        {
            this.smpTriangle += this.triValue << 1;
            this.smpDmc += this.dmc.sample << 1;
            this.smpSquare1 += this.square1.sampleValue << 1;
            this.smpSquare2 += this.square2.sampleValue << 1;
            this.accCount += 2;
        }
        else if (cycles == 4)
        {
            this.smpTriangle += this.triValue << 2;
            this.smpDmc += this.dmc.sample << 2;
            this.smpSquare1 += this.square1.sampleValue << 2;
            this.smpSquare2 += this.square2.sampleValue << 2;
            this.accCount += 4;
        }
        else
        {
            this.smpTriangle += cycles * this.triValue;
            this.smpDmc += cycles * this.dmc.sample;
            this.smpSquare1 += cycles * this.square1.sampleValue;
            this.smpSquare2 += cycles * this.square2.sampleValue;
            this.accCount += cycles;
        }
    }

    public void clockFrameCounter(int nCycles)
    {
        if ((this.initCounter > 0) && this.initingHardware)
        {
            this.initCounter -= nCycles;
            if (this.initCounter <= 0)
            {
                this.initingHardware = false;
            }
        }
        else
        {
            nCycles += this.extraCycles;
            this.maxCycles = this.sampleTimerMax - this.sampleTimer;
            if ((nCycles << 10) > this.maxCycles)
            {
                this.extraCycles = ((nCycles << 10) - this.maxCycles) >> 10;
                nCycles -= this.extraCycles;
            }
            else
            {
                this.extraCycles = 0;
            }
            if (this.dmc.isEnabled_Renamed_Field)
            {
                this.dmc.shiftCounter -= nCycles << 3;
                while ((this.dmc.shiftCounter <= 0) && (this.dmc.dmaFrequency > 0))
                {
                    this.dmc.shiftCounter += this.dmc.dmaFrequency;
                    this.dmc.clockDmc();
                }
            }
            if (this.triangle.progTimerMax > 0)
            {
                this.triangle.progTimerCount -= nCycles;
                while (this.triangle.progTimerCount <= 0)
                {
                    this.triangle.progTimerCount += this.triangle.progTimerMax + 1;
                    if ((this.triangle.linearCounter > 0) && (this.triangle.lengthCounter > 0))
                    {
                        this.triangle.triangleCounter++;
                        this.triangle.triangleCounter &= 0x1f;
                        if (this.triangle.isEnabled_Renamed_Field)
                        {
                            if (this.triangle.triangleCounter >= 0x10)
                            {
                                this.triangle.sampleValue = this.triangle.triangleCounter & 15;
                            }
                            else
                            {
                                this.triangle.sampleValue = 15 - (this.triangle.triangleCounter & 15);
                            }
                            this.triangle.sampleValue = this.triangle.sampleValue << 4;
                        }
                    }
                }
            }
            this.square1.progTimerCount -= nCycles;
            if (this.square1.progTimerCount <= 0)
            {
                this.square1.progTimerCount += (this.square1.progTimerMax + 1) << 1;
                this.square1.squareCounter++;
                this.square1.squareCounter &= 7;
                this.square1.updateSampleValue();
            }
            this.square2.progTimerCount -= nCycles;
            if (this.square2.progTimerCount <= 0)
            {
                this.square2.progTimerCount += (this.square2.progTimerMax + 1) << 1;
                this.square2.squareCounter++;
                this.square2.squareCounter &= 7;
                this.square2.updateSampleValue();
            }
            int num = nCycles;
            if ((this.noise.progTimerCount - num) <= 0)
            {
                while (num-- > 0)
                {
                    if ((--this.noise.progTimerCount <= 0) && (this.noise.progTimerMax > 0))
                    {
                        this.noise.shiftReg = this.noise.shiftReg << 1;
                        this.noise.tmp = ((this.noise.shiftReg << ((this.noise.randomMode == null) ? 1 : 6)) ^ this.noise.shiftReg) & 0x8000;
                        if (this.noise.tmp != 0)
                        {
                            this.noise.shiftReg |= 1;
                            this.noise.randomBit = 0;
                            this.noise.sampleValue = 0;
                        }
                        else
                        {
                            this.noise.randomBit = 1;
                            if (this.noise.isEnabled_Renamed_Field && (this.noise.lengthCounter > 0))
                            {
                                this.noise.sampleValue = this.noise.masterVolume;
                            }
                            else
                            {
                                this.noise.sampleValue = 0;
                            }
                        }
                        this.noise.progTimerCount += this.noise.progTimerMax;
                    }
                    this.noise.accValue += this.noise.sampleValue;
                    this.noise.accCount += 1L;
                }
            }
            else
            {
                this.noise.progTimerCount -= num;
                this.noise.accCount += num;
                this.noise.accValue += num * this.noise.sampleValue;
            }
            if (this.frameIrqEnabled && this.frameIrqActive)
            {
                this.nes.cpu.requestIrq(0);
            }
            this.masterFrameCounter += nCycles << 1;
            if (this.masterFrameCounter >= this.frameTime)
            {
                this.masterFrameCounter -= this.frameTime;
                this.frameCounterTick();
            }
            this.accSample(nCycles);
            this.sampleTimer += nCycles << 10;
            if (this.sampleTimer >= this.sampleTimerMax)
            {
                this.sample();
                this.sampleTimer -= this.sampleTimerMax;
            }
        }
    }

    public void destroy()
    {
        this.nes = null;
        this.cpuMem = null;
        if (this.square1 != null)
        {
            this.square1.destroy();
        }
        if (this.square2 != null)
        {
            this.square2.destroy();
        }
        if (this.triangle != null)
        {
            this.triangle.destroy();
        }
        if (this.noise != null)
        {
            this.noise.destroy();
        }
        if (this.dmc != null)
        {
            this.dmc.destroy();
        }
        this.square1 = null;
        this.square2 = null;
        this.triangle = null;
        this.noise = null;
        this.dmc = null;
        this.line = null;
    }

    public void frameCounterTick()
    {
        this.derivedFrameCounter++;
        if (this.derivedFrameCounter >= this.frameIrqCounterMax)
        {
            this.derivedFrameCounter = 0;
        }
        if ((this.derivedFrameCounter == 1) || (this.derivedFrameCounter == 3))
        {
            this.triangle.clockLengthCounter();
            this.square1.clockLengthCounter();
            this.square2.clockLengthCounter();
            this.noise.clockLengthCounter();
            this.square1.clockSweep();
            this.square2.clockSweep();
        }
        if ((this.derivedFrameCounter >= 0) && (this.derivedFrameCounter < 4))
        {
            this.square1.clockEnvDecay();
            this.square2.clockEnvDecay();
            this.noise.clockEnvDecay();
            this.triangle.clockLinearCounter();
        }
        if ((this.derivedFrameCounter == 3) && (this.countSequence == 0))
        {
            this.frameIrqActive = true;
        }
    }

    public int getDmcFrequency(int value_Renamed)
    {
        if ((value_Renamed >= 0) && (value_Renamed < 0x10))
        {
            return this.dmcFreqLookup[value_Renamed];
        }
        return 0;
    }

    public int getLengthMax(int value_Renamed)
    {
        return this.LengthLookup[value_Renamed >> 3];
    }

    public int getMillisToAvailableAbove(int target_avail)
    {
        int num2 = 0;
        if ((num2 = this.line._waveFormat.BitsPerSample) >= target_avail)
        {
            return 0;
        }
        long num = ((target_avail - num2) * 0x3e8) / this.sampleRate;
        num /= this.stereo ? ((long) 4) : ((long) 2);
        num /= 10L;
        num *= 10L;
        return (int) num;
    }

    public int getNoiseWaveLength(int value_Renamed)
    {
        if ((value_Renamed >= 0) && (value_Renamed < 0x10))
        {
            return this.noiseWaveLengthLookup[value_Renamed];
        }
        return 0;
    }

    public int getSampleRate()
    {
        return this.sampleRate;
    }

    public void initDACtables()
    {
        double num;
        int num2;
        this.square_table = new int[0x200];
        this.tnd_table = new int[0xcc0];
        int num3 = 0;
        int num4 = 0;
        for (int i = 0; i < 0x200; i++)
        {
            num = 95.52 / ((8128.0 / (((double) i) / 16.0)) + 100.0);
            num *= 0.98411;
            num *= 50000.0;
            num2 = (int) num;
            this.square_table[i] = num2;
            if (num2 > num3)
            {
                num3 = num2;
            }
        }
        for (int j = 0; j < 0xcc0; j++)
        {
            num = 163.67 / ((24329.0 / (((double) j) / 16.0)) + 100.0);
            num *= 0.98411;
            num *= 50000.0;
            num2 = (int) num;
            this.tnd_table[j] = num2;
            if (num2 > num4)
            {
                num4 = num2;
            }
        }
        this.dacRange = num3 + num4;
        this.dcValue = this.dacRange / 2;
    }

    public void initDmcFrequencyLookup()
    {
        this.dmcFreqLookup = new int[] { 0xd60, 0xbe0, 0xaa0, 0xa00, 0x8f0, 0x7f0, 0x710, 0x6b0, 0x5f0, 0x500, 0x470, 0x400, 0x350, 0x2a0, 0x240, 0x1b0 };
    }

    public void initLengthLookup()
    {
        this.LengthLookup = new int[] { 
            10, 0xfe, 20, 2, 40, 4, 80, 6, 160, 8, 60, 10, 14, 12, 0x1a, 14, 
            12, 0x10, 0x18, 0x12, 0x30, 20, 0x60, 0x16, 0xc0, 0x18, 0x48, 0x1a, 0x10, 0x1c, 0x20, 30
         };
    }

    public void initNoiseWaveLengthLookup()
    {
        this.noiseWaveLengthLookup = new int[] { 4, 8, 0x10, 0x20, 0x40, 0x60, 0x80, 160, 0xca, 0xfe, 380, 0x1fc, 0x2fa, 0x3f8, 0x7f2, 0xfe4 };
    }

    public short readReg(int address)
    {
        int num = 0;
        num |= this.square1.LengthStatus;
        num |= this.square2.LengthStatus << 1;
        num |= this.triangle.LengthStatus << 2;
        num |= this.noise.LengthStatus << 3;
        num |= this.dmc.LengthStatus << 4;
        num |= (((this.frameIrqActive != null) && (this.frameIrqEnabled != null)) ? 1 : 0) << 6;
        num |= this.dmc.IrqStatus << 7;
        this.frameIrqActive = false;
        this.dmc.irqGenerated = false;
        return (short) num;
    }

    public void reset()
    {
        this.setSampleRate(this.sampleRate, false);
        this.updateChannelEnable(0);
        this.masterFrameCounter = 0;
        this.derivedFrameCounter = 0;
        this.countSequence = 0;
        this.sampleCount = 0;
        this.initCounter = 0x800;
        this.frameIrqEnabled = false;
        this.initingHardware = false;
        this.resetCounter();
        this.square1.reset();
        this.square2.reset();
        this.triangle.reset();
        this.noise.reset();
        this.dmc.reset();
        this.bufferIndex = 0;
        this.accCount = 0;
        this.smpSquare1 = 0;
        this.smpSquare2 = 0;
        this.smpTriangle = 0;
        this.smpNoise = 0;
        this.smpDmc = 0;
        this.frameIrqEnabled = false;
        this.frameIrqCounterMax = 4;
        this.channelEnableValue = 0xff;
        this.b1 = 0;
        this.b2 = 0;
        this.startedPlaying = false;
        this.sampleValueL = 0;
        this.sampleValueR = 0;
        this.prevSampleL = 0;
        this.prevSampleR = 0;
        this.smpAccumL = 0;
        this.smpAccumR = 0;
        this.smpDiffL = 0;
        this.smpDiffR = 0;
    }

    public void resetCounter()
    {
        if (this.countSequence == 0)
        {
            this.derivedFrameCounter = 4;
        }
        else
        {
            this.derivedFrameCounter = 0;
        }
    }

    public void sample()
    {
        if (this.accCount > 0)
        {
            this.smpSquare1 = this.smpSquare1 << 4;
            this.smpSquare1 /= this.accCount;
            this.smpSquare2 = this.smpSquare2 << 4;
            this.smpSquare2 /= this.accCount;
            this.smpTriangle /= this.accCount;
            this.smpDmc = this.smpDmc << 4;
            this.smpDmc /= this.accCount;
            this.accCount = 0;
        }
        else
        {
            this.smpSquare1 = this.square1.sampleValue << 4;
            this.smpSquare2 = this.square2.sampleValue << 4;
            this.smpTriangle = this.triangle.sampleValue;
            this.smpDmc = this.dmc.sample << 4;
        }
        this.smpNoise = (int) ((this.noise.accValue << 4) / this.noise.accCount);
        this.noise.accValue = this.smpNoise >> 4;
        this.noise.accCount = 1L;
        if (this.stereo)
        {
            this.sq_index = ((this.smpSquare1 * this.stereoPosLSquare1) + (this.smpSquare2 * this.stereoPosLSquare2)) >> 8;
            this.tnd_index = ((((3 * this.smpTriangle) * this.stereoPosLTriangle) + ((this.smpNoise << 1) * this.stereoPosLNoise)) + (this.smpDmc * this.stereoPosLDMC)) >> 8;
            if (this.sq_index >= this.square_table.Length)
            {
                this.sq_index = this.square_table.Length - 1;
            }
            if (this.tnd_index >= this.tnd_table.Length)
            {
                this.tnd_index = this.tnd_table.Length - 1;
            }
            this.sampleValueL = (this.square_table[this.sq_index] + this.tnd_table[this.tnd_index]) - this.dcValue;
            this.sq_index = ((this.smpSquare1 * this.stereoPosRSquare1) + (this.smpSquare2 * this.stereoPosRSquare2)) >> 8;
            this.tnd_index = ((((3 * this.smpTriangle) * this.stereoPosRTriangle) + ((this.smpNoise << 1) * this.stereoPosRNoise)) + (this.smpDmc * this.stereoPosRDMC)) >> 8;
            if (this.sq_index >= this.square_table.Length)
            {
                this.sq_index = this.square_table.Length - 1;
            }
            if (this.tnd_index >= this.tnd_table.Length)
            {
                this.tnd_index = this.tnd_table.Length - 1;
            }
            this.sampleValueR = (this.square_table[this.sq_index] + this.tnd_table[this.tnd_index]) - this.dcValue;
        }
        else
        {
            this.sq_index = this.smpSquare1 + this.smpSquare2;
            this.tnd_index = ((3 * this.smpTriangle) + (2 * this.smpNoise)) + this.smpDmc;
            if (this.sq_index >= this.square_table.Length)
            {
                this.sq_index = this.square_table.Length - 1;
            }
            if (this.tnd_index >= this.tnd_table.Length)
            {
                this.tnd_index = this.tnd_table.Length - 1;
            }
            this.sampleValueL = 3 * ((this.square_table[this.sq_index] + this.tnd_table[this.tnd_index]) - this.dcValue);
            this.sampleValueL = this.sampleValueL >> 2;
        }
        this.smpDiffL = this.sampleValueL - this.prevSampleL;
        this.prevSampleL += this.smpDiffL;
        this.smpAccumL += this.smpDiffL - (this.smpAccumL >> 10);
        this.sampleValueL = this.smpAccumL;
        if (this.stereo)
        {
            this.smpDiffR = this.sampleValueR - this.prevSampleR;
            this.prevSampleR += this.smpDiffR;
            this.smpAccumR += this.smpDiffR - (this.smpAccumR >> 10);
            this.sampleValueR = this.smpAccumR;
            if ((this.bufferIndex + 4) < this.sampleBuffer.Length)
            {
                this.sampleBuffer[this.bufferIndex++] = (byte) (this.sampleValueL & 0xff);
                this.sampleBuffer[this.bufferIndex++] = (byte) ((this.sampleValueL >> 8) & 0xff);
                this.sampleBuffer[this.bufferIndex++] = (byte) (this.sampleValueR & 0xff);
                this.sampleBuffer[this.bufferIndex++] = (byte) ((this.sampleValueR >> 8) & 0xff);
            }
        }
        else if ((this.bufferIndex + 2) < this.sampleBuffer.Length)
        {
            this.sampleBuffer[this.bufferIndex++] = (byte) (this.sampleValueL & 0xff);
            this.sampleBuffer[this.bufferIndex++] = (byte) ((this.sampleValueL >> 8) & 0xff);
        }
        this.smpSquare1 = 0;
        this.smpSquare2 = 0;
        this.smpTriangle = 0;
        this.smpDmc = 0;
    }

    public void setChannelEnabled(int channel, bool value_Renamed)
    {
        if (channel == 0)
        {
            this.userEnableSquare1 = value_Renamed;
        }
        else if (channel == 1)
        {
            this.userEnableSquare2 = value_Renamed;
        }
        else if (channel == 2)
        {
            this.userEnableTriangle = value_Renamed;
        }
        else if (channel == 3)
        {
            this.userEnableNoise = value_Renamed;
        }
        else
        {
            this.userEnableDmc = value_Renamed;
        }
        this.updateChannelEnable(this.channelEnableValue);
    }

    public void setSampleRate(int rate, bool restart)
    {
        lock (this)
        {
            bool running = this.nes.Running;
            if (running)
            {
                this.nes.stopEmulation();
            }
            this.sampleRate = rate;
            this.sampleTimerMax = (int) (((1024.0 * Globals.CPU_FREQ_NTSC) * Globals.preferredFrameRate) / (this.sampleRate * 60.0));
            this.frameTime = (int) ((14915.0 * Globals.preferredFrameRate) / 60.0);
            this.sampleTimer = 0;
            this.bufferIndex = 0;
            if (restart)
            {
                this.stop();
                this.start();
            }
            if (running)
            {
                this.nes.startEmulation();
            }
        }
    }

    public void setStereo(bool s, bool restart)
    {
        lock (this)
        {
            if (this.stereo != s)
            {
                bool running = this.nes.Running;
                this.nes.stopEmulation();
                this.stereo = s;
                if (this.stereo)
                {
                    this.sampleBuffer = new byte[this.bufferSize * 4];
                }
                else
                {
                    this.sampleBuffer = new byte[this.bufferSize * 2];
                }
                if (restart)
                {
                    this.stop();
                    this.start();
                }
                if (running)
                {
                    this.nes.startEmulation();
                }
            }
        }
    }

    public void start()
    {
        this.line = new MyMediaStreamSource();
        this.bufferIndex = 0;
        this.isRunning = true;
    }

    public void stateLoad(ByteBuffer buf)
    {
    }

    public void stateSave(ByteBuffer buf)
    {
    }

    public void stop()
    {
        if (this.line != null)
        {
            this.line = null;
        }
    }

    public void updateChannelEnable(int value_Renamed)
    {
        this.channelEnableValue = (short) value_Renamed;
        this.square1.Enabled = this.userEnableSquare1 && ((value_Renamed & 1) != 0);
        this.square2.Enabled = this.userEnableSquare2 && ((value_Renamed & 2) != 0);
        this.triangle.Enabled = this.userEnableTriangle && ((value_Renamed & 4) != 0);
        this.noise.Enabled = this.userEnableNoise && ((value_Renamed & 8) != 0);
        this.dmc.Enabled = this.userEnableDmc && ((value_Renamed & 0x10) != 0);
    }

    public void updateStereoPos()
    {
        this.stereoPosLSquare1 = (this.panning[0] * this.masterVolume) >> 8;
        this.stereoPosLSquare2 = (this.panning[1] * this.masterVolume) >> 8;
        this.stereoPosLTriangle = (this.panning[2] * this.masterVolume) >> 8;
        this.stereoPosLNoise = (this.panning[3] * this.masterVolume) >> 8;
        this.stereoPosLDMC = (this.panning[4] * this.masterVolume) >> 8;
        this.stereoPosRSquare1 = this.masterVolume - this.stereoPosLSquare1;
        this.stereoPosRSquare2 = this.masterVolume - this.stereoPosLSquare2;
        this.stereoPosRTriangle = this.masterVolume - this.stereoPosLTriangle;
        this.stereoPosRNoise = this.masterVolume - this.stereoPosLNoise;
        this.stereoPosRDMC = this.masterVolume - this.stereoPosLDMC;
    }

    public void writeBuffer()
    {
        if (this.line != null)
        {
            this.bufferIndex -= this.bufferIndex % (this.stereo ? 4 : 2);
            this.line.write(this.sampleBuffer, 0, this.bufferIndex);
            this.bufferIndex = 0;
        }
    }

    public void writeReg(int address, short value_Renamed)
    {
        if ((address >= 0x4000) && (address < 0x4004))
        {
            this.square1.writeReg(address, value_Renamed);
        }
        else if ((address >= 0x4004) && (address < 0x4008))
        {
            this.square2.writeReg(address, value_Renamed);
        }
        else if ((address >= 0x4008) && (address < 0x400c))
        {
            this.triangle.writeReg(address, value_Renamed);
        }
        else if ((address >= 0x400c) && (address <= 0x400f))
        {
            this.noise.writeReg(address, value_Renamed);
        }
        else if (address == 0x4010)
        {
            this.dmc.writeReg(address, value_Renamed);
        }
        else if (address == 0x4011)
        {
            this.dmc.writeReg(address, value_Renamed);
        }
        else if (address == 0x4012)
        {
            this.dmc.writeReg(address, value_Renamed);
        }
        else if (address == 0x4013)
        {
            this.dmc.writeReg(address, value_Renamed);
        }
        else if (address == 0x4015)
        {
            this.updateChannelEnable(value_Renamed);
            if ((value_Renamed != 0) && (this.initCounter > 0))
            {
                this.initingHardware = true;
            }
            this.dmc.writeReg(address, value_Renamed);
        }
        else if (address == 0x4017)
        {
            this.countSequence = (value_Renamed >> 7) & 1;
            this.masterFrameCounter = 0;
            this.frameIrqActive = false;
            if (((value_Renamed >> 6) & 1) == 0)
            {
                this.frameIrqEnabled = true;
            }
            else
            {
                this.frameIrqEnabled = false;
            }
            if (this.countSequence == 0)
            {
                this.frameIrqCounterMax = 4;
                this.derivedFrameCounter = 4;
            }
            else
            {
                this.frameIrqCounterMax = 5;
                this.derivedFrameCounter = 0;
                this.frameCounterTick();
            }
        }
    }

    public int BufferPos
    {
        get
        {
            return this.bufferIndex;
        }
    }

    public MyMediaStreamSource Line
    {
        get
        {
            return this.line;
        }
    }

    public int MasterVolume
    {
        set
        {
            if (value < 0)
            {
                value = 0;
            }
            if (value > 0x100)
            {
                value = 0x100;
            }
            this.masterVolume = value;
            this.updateStereoPos();
        }
    }

    public NES Nes
    {
        get
        {
            return this.nes;
        }
    }

    public int[] Panning
    {
        set
        {
            for (int i = 0; i < 5; i++)
            {
                this.panning[i] = value[i];
            }
            this.updateStereoPos();
        }
    }

    public int PapuBufferSize
    {
        get
        {
            return this.sampleBuffer.Length;
        }
    }
}

